// balm_category.h                                                    -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BALM_CATEGORY
#define INCLUDED_BALM_CATEGORY

#include <bsls_ident.h>
BSLS_IDENT("$Id: balm_category.h,v 1.4 2008/04/16 20:00:49 hversche Exp $")

//@PURPOSE: Provide a representation of a metric category.
//
//@CLASSES:
//    balm::Category: a representation of a metric category
//
//@SEE_ALSO: balm_metricregistry, balm_metricid, balm_metricdescription
//
//@DESCRIPTION: This component provides a class, 'balm::Category', whose values
// are used to categorize collected metrics.  A metric "category" is an
// identifier (chosen by the application) that groups together one or more
// metrics.  A 'balm::Category' object contains the address of a string holding
// the name of the category and a boolean value indicating whether the
// category is currently enabled.  The 'balm::Category' class suppresses copy
// construction and assignment, and does not provide equality operators;
// applications should use a *single* 'balm::Category' object instance per
// category (such as one provided by the *'balm::MetricRegistry'* component).
//
// IMPORTANT: The category name, whose type is 'const char *', must remain
// constant and valid throughout the lifetime of the 'balm::Category' object.
//
///Alternative Systems for Telemetry
///---------------------------------
// Bloomberg software may alternatively use the GUTS telemetry API, which is
// integrated into Bloomberg infrastructure.
//
///Thread Safety
///-------------
// 'balm::Category' is generally *const* *thread-safe*, meaning that accessors
// may be invoked concurrently from different threads, but it is not safe to
// access or modify a 'balm::Category' in one thread while another thread
// modifies the same object.  However, it is safe to *access* the 'enabled'
// property on one (or more) thread(s) while the object is being modified on
// another thread.
//
///Usage
///-----
// The following example demonstrates how to create, access, and modify a
// 'balm::Category' object.  We start by creating three category objects with
// different names:
//..
//  balm::Category categoryA("A", true);
//  balm::Category categoryB("B", false);
//  balm::Category categoryC("C");
//..
// Once the category objects have been created, we can use the 'name' and
// 'enabled' methods to access their values:
//..
//  assert(0 == bsl::strcmp("A", categoryA.name()));
//  assert(0 == bsl::strcmp("B", categoryB.name()));
//  assert(0 == bsl::strcmp("C", categoryC.name()));
//
//  assert( categoryA.enabled());
//  assert(!categoryB.enabled());
//  assert( categoryC.enabled());
//..
// Finally, we modify the enablement status of one of the categories, and then
// write all three categories to the console:
//..
//  categoryC.setEnabled(false);
//
//  bsl::cout << "categoryA: " << categoryA << bsl::endl
//            << "categoryB: " << categoryB << bsl::endl
//            << "categoryC: " << categoryC << bsl::endl;
//..
// With the resulting console output:
//..
//  categoryA: [ A ENABLED ]
//  categoryB: [ B DISABLED ]
//  categoryC: [ C DISABLED ]
//..

#include <balscm_version.h>

#include <bsl_iosfwd.h>

#include <bsls_atomic.h>

namespace BloombergLP {


namespace balm {

class CategoryHolder;

                               // ==============
                               // class Category
                               // ==============

class Category {
    // This class provides a mechanism for representing a category.  A category
    // is an identifier used to group related metrics.  A 'Category' object
    // contains the address of a null-terminated string, 'name', holding the
    // name of the category and a boolean value, 'enabled', indicating whether
    // the category is currently enabled.

    // DATA
    const char     *d_name_p;     // name of the category (held, not owned)

    bsls::AtomicInt d_enabled;    // whether the category is enabled

    CategoryHolder *d_holders_p;  // linked list of holders of this category

    // NOT IMPLEMENTED
    Category(const Category& );
    Category& operator=(const Category& );

  public:
    // CREATORS
    explicit Category(const char *name, bool enabledFlag = true);
        // Create a category having the specified 'name' address.  Optionally
        // specify 'enabledFlag', the enabled status of the category; if
        // 'enabledFlag' is not specified, the 'enabled' status is 'true'.  The
        // behavior is undefined unless 'name' remains valid and unmodified for
        // the lifetime of this object.

    ~Category();
        // Destroy this category object.

    // MANIPULATORS
    void setName(const char *name);
        // Set the name of this metric to the specified 'name' address.  The
        // behavior is undefined unless 'name' remains valid and unmodified
        // for the lifetime of this object.

    void setEnabled(bool enabledFlag);
        // Set the 'enabled' state of this category to the value of the
        // specified 'enabledFlag' and update any 'CategoryHolder' objects
        // registered with this category.  Note that this operation is *not*
        // atomic, and other threads may simultaneously access the current
        // enabled value while this operation is performed.  Also note that
        // this operation has *linear* performance with respect to the number
        // of registered category holders for 'category'.

    void registerCategoryHolder(CategoryHolder *holder);
        // Load into the specified 'holder' the address of this category, its
        // 'enabled()' status, and the address of the next holder in the
        // linked list of category holders maintained by this object
        // (prepending 'holder' to this category's linked list of category
        // holders).  This category will update 'holder->enabled()' when its
        // enabled state changes, and will reset 'holder' (i.e.,
        // 'holder->reset()') when this category is destroyed.  The behavior
        // is undefined unless 'holder' remains valid and *unmodified* (by the
        // client) for the lifetime of this object and is *not* registered
        // again with any category (including this one).

    // ACCESSORS
    const char *name() const;
        // Return the address of the non-modifiable null-terminated string
        // containing the name of this category.

    bool enabled() const;
        // Report whether this category is enabled. This function is fully
        // thread-safe.

    const bsls::AtomicInt& isEnabledRaw() const;
        // Return a *reference* to a const value indicating the enabled status
        // of this category, allowing downstream uses to minimize latency by
        // avoiding indirection through abstracted interfaces, albeit at some
        // risk of object-lifetime violations.  The returned reference must not
        // be allowed to outlive this category object.

    bsl::ostream& print(bsl::ostream& stream) const;
        // Print this category to the specified output 'stream' in some human
        // readable form, and return the modifiable 'stream'.
};

// ============================================================================
//                            INLINE DEFINITIONS
// ============================================================================

// FREE OPERATORS
inline
bsl::ostream& operator<<(bsl::ostream& stream, const Category& rhs);
    // Output a formatted description of the specified 'rhs' category to the
    // specified 'stream', and return the modifiable 'stream'.

                            // ====================
                            // class CategoryHolder
                            // ====================

class CategoryHolder {
    // This class, informally referred to as a "category holder" (or simply
    // "holder"), holds a category, an enabled value, and a pointer to a
    // "next" holder.  Both the category and next pointer may be null.  The
    // intended use is as follows: (1) instances of this class are (only)
    // declared in contexts where collecting a metric occurs; (2) if the
    // enabled value is true, the category contains the address of a valid,
    // enabled, category; (3) if the next pointer is non-null, then the holder
    // pointed to holds the same category and threshold.  Instances of this
    // class must be *statically* initializable.  Hence, the data members are
    // 'public', and no constructors or destructor are defined.
    //
    // This class should *not* be used directly by client code.  It is an
    // implementation detail of the 'balm' metric collection system.

    // NOT IMPLEMENTED
    CategoryHolder& operator=(const CategoryHolder& rhs);

  public:

    // PUBLIC DATA MEMBERS
    bool            d_enabled;     // whether the category is enabled
    const Category *d_category_p;  // held category (not owned)
    CategoryHolder *d_next_p;      // next category holder in linked list

    // CREATORS

    // No constructors or destructors are declared in order to allow for static
    // initialization of instances of this class.

    // MANIPULATORS
    void reset();
        // Reset this object to its default value.  The default value is:
        //..
        //   { false, 0, 0 }
        //..

    void setCategory(const Category *category);
        // Set the address of the category held by this holder to the specified
        // 'category'.

    void setEnabled(bool enabledFlag);
        // Set the 'enabled' state of this category to the value of the
        // specified 'enabledFlag'.

    void setNext(CategoryHolder *holder);
        // Set this holder to point to the specified 'holder'.

    // ACCESSORS
    const Category *category() const;
        // Return the address of the non-modifiable category held by this
        // holder.

    bool enabled() const;
        // Return 'true' if 'category' is valid (i.e., non-null) and enabled,
        // and 'false' otherwise.

    CategoryHolder *next() const;
        // Return the address of the modifiable holder pointed to by this
        // holder.
};

// ============================================================================
//                        INLINE FUNCTION DEFINITIONS
// ============================================================================

                               // --------------
                               // class Category
                               // --------------

// CREATORS
inline
Category::Category(const char *name, bool enabledFlag)
: d_name_p(name)
, d_enabled(enabledFlag)
, d_holders_p(0)
{
}

// MANIPULATORS
inline
void Category::setName(const char *name)
{
    d_name_p = name;
}

// ACCESSORS
inline
const char *Category::name() const
{
    return d_name_p;
}

inline
bool Category::enabled() const
{
    return d_enabled;
}

inline
const bsls::AtomicInt& Category::isEnabledRaw() const
{
    return d_enabled;
}

                            // --------------------
                            // class CategoryHolder
                            // --------------------

// MANIPULATORS
inline
void CategoryHolder::setCategory(const Category *category)
{
    d_category_p = category;
}

inline
void CategoryHolder::setEnabled(bool enabledFlag)
{
    d_enabled = enabledFlag;
}

inline
void CategoryHolder::setNext(CategoryHolder *holder)
{
    d_next_p = holder;
}

// ACCESSORS
inline
const Category *CategoryHolder::category() const
{
    return d_category_p;
}

inline
bool CategoryHolder::enabled() const
{
    return d_enabled;
}

inline
CategoryHolder *CategoryHolder::next() const
{
    return d_next_p;
}
}  // close package namespace

// FREE OPERATORS
inline
bsl::ostream& balm::operator<<(bsl::ostream& stream, const Category& rhs)
{
    return rhs.print(stream);
}

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
